package com.framework.service.other.interceptor;

import com.framework.common.model.auth.IgnoredRoleAuth;
import com.framework.common.util.reflect.ReflectUtil;
import com.framework.common.util.threadlocal.ThreadLocalUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;


/**
 * @author 龘鵺
 * @version 1.0
 * @className com.framework.service.other.interceptor
 * @description 租户拦截器sql处理类
 * @datetime 2025-05-30 16:03:32
 * 参数查询：
 * https://blog.csdn.net/qq_35807136/article/details/79052052
 * https://blog.csdn.net/weixin_45287472/article/details/132999257
 * 借鉴：
 * 搜索关键字：springboot Mybatis 全局拦截SQL 增删改查 添加租户
 * https://blog.csdn.net/a772304419/article/details/145882103
 * https://blog.csdn.net/tiantiantbtb/article/details/140660285
 * https://www.cnblogs.com/mimimikasa/p/15531538.html
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
@Component
public class TenantInterceptor implements Interceptor {
    @Autowired
    private IgnoredRoleAuth ignoredRoleAuth;
    private final static String entity_name = "tenantId";
    private final static String sql_name = "TENANT_ID";
    private final static String where_name = "WHERE";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if ((ThreadLocalUtil.getTenantId() == null || ThreadLocalUtil.getTenantId().intValue() < 1) || (StringUtils.isEmpty(ThreadLocalUtil.getRoleCode()) || ignoredRoleAuth.getAuthList().contains(ThreadLocalUtil.getRoleCode()))) {
            // 继续执行原始操作
            return invocation.proceed();
        }
        // 获取原始的 SQL 语句和参数
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 修改 SQL 语句或参数（例如添加租户ID）
        BoundSql boundSql = statementHandler.getBoundSql();
        // 获取sql参数对象，可能是map|指定实体类
        Object parameter = boundSql.getParameterObject();
        if (parameter == null) {
            // 继续执行原始操作
            return invocation.proceed();
        }

        Field field = null;
        if (parameter instanceof Map) {
            Map map = (Map) parameter;
            if (!map.containsKey(entity_name)) {
                map.put(entity_name, ThreadLocalUtil.getTenantId());
            }
        } else {
            field = getField(parameter, entity_name);
        }

        if (field == null) {
            return invocation.proceed();
        }
        ReflectUtil.invokeSet(parameter, entity_name, ThreadLocalUtil.getTenantId());
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        // 先判断是不是SELECT操作
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
        if (!SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType()) && !SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
            // 继续执行原始操作
            return invocation.proceed();
        }
//            System.out.println("TenantInterceptor.type = " + mappedStatement.getSqlCommandType());
//            if (SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
//                //SELECT=查询 - 因为查询的时候where条件中就已经带了租户判断的。前面有参数映射赋值。所以不用额外增加租户查询条件
//                System.out.println("SELECT");
//                originalSql = addQueryWhereTenantCondition(originalSql);
//            }
//            if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
//                //INSERT=新增- 因为新增不需要单独设置参数。 sql语句用已经自带。 如果是其他开源项目就不确定了
//                System.out.println("INSERT");
//            }
        String originalSql = boundSql.getSql();
//        if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())) {
        //UPDATE=修改
//                System.out.println("UPDATE");
        originalSql = addEditWhereTenantCondition(originalSql);
        //添加修改后的sql语句
        metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
//        }
//        if (SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) {
//            //DELETE=删除
////                System.out.println("DELETE");
//            originalSql = addEditWhereTenantCondition(originalSql);
//            //添加修改后的sql语句
//            metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
////            System.out.println("TenantInterceptor.intercept = " + originalSql);
//        }
        // 继续执行原始操作
        return invocation.proceed();
    }

    /**
     * @param originalSql 1 原始sql语句
     * @return java.lang.String 返回增加后的sql语句
     * @title 查询，增加租户条件
     * @description 查询，增加租户条件
     * @author 龘鵺
     * @dateTime 2025-05-31 23-10-52
     */
    private String addQueryWhereTenantCondition(String originalSql) {
        //对于其他 SQL 语句，添加 TENANT_ID 条件
        String modifiedSql;
        if (originalSql.toLowerCase().contains(where_name)) {
            modifiedSql = originalSql + " AND p." + sql_name + " = " + ThreadLocalUtil.getTenantId();
        } else {
            modifiedSql = originalSql + " " + where_name + " p." + sql_name + " = " + ThreadLocalUtil.getTenantId();
        }
        return modifiedSql;
    }

    /**
     * @param originalSql 1 原始sql语句
     * @return java.lang.String 返回增加后的sql语句
     * @title 新增，修改，删除，增加租户条件
     * @description 新增，修改，删除，增加租户条件
     * @author 龘鵺
     * @dateTime 2025-05-31 23-10-52
     */
    private String addEditWhereTenantCondition(String originalSql) {
        //对于其他 SQL 语句，添加 TENANT_ID 条件
        String modifiedSql;
        if (originalSql.toLowerCase().contains(where_name)) {
            modifiedSql = originalSql + " AND " + sql_name + " = " + ThreadLocalUtil.getTenantId();
        } else {
            modifiedSql = originalSql + " " + where_name + " " + sql_name + " = " + ThreadLocalUtil.getTenantId();
        }
        return modifiedSql;
    }

    /**
     * @param obj       1 类对象
     * @param fieldName 2 变量名
     * @return java.lang.reflect.Field
     * @title 获取对象中的字段
     * @description 获取对象中的字段
     * @author 龘鵺
     * @datetime 2022-01-09 11:11:11
     */
    protected Field getField(Object obj, String fieldName) throws NoSuchFieldException {
        Class clzz = obj.getClass();
        Field[] fields = clzz.getDeclaredFields();
        Field dest = null;
        while (!hasField(fields, fieldName) && !clzz.getName().equalsIgnoreCase("java.lang.Object")) {
            clzz = clzz.getSuperclass();
            if (!clzz.getName().equalsIgnoreCase("java.lang.Object")) {
                fields = clzz.getDeclaredFields();
            }
        }
        if (StringUtils.isNotEmpty(fieldName)) {
            if (hasField(fields, fieldName)) {
                dest = clzz.getDeclaredField(fieldName);
            }
        }
        return dest;
    }


    /**
     * @param fields    1 类的字段数组
     * @param fieldName 2 分页注解类对象
     * @return boolean
     * @title 判断对象中是否有要找的字段
     * @description 判断对象中是否有要找的字段
     * @author 龘鵺
     * @datetime 2022-01-09 11:11:11
     */
    private boolean hasField(Field[] fields, String fieldName) {
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(fieldName)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}
